package topoSort;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;

public class Edge extends Line2D.Double implements Selectable
{
	public static final int EDGE_WIDTH = 5;
	public static final int ARROW_LENGHT = 20;
	public static final double ARROW_ANGLE = Math.PI / 6;
	private static BasicStroke m_stroke = new BasicStroke(EDGE_WIDTH,
			BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND);
	private static final long serialVersionUID = -324115406668386727L;
	private Vertex m_from;
	private Vertex m_to;
	private Line2D.Double m_arrowSideL, m_arrowSideR;
	private boolean m_selected = false;
	private Color m_color = Color.BLACK;
	private Color m_selectionColor = Color.GRAY;

	Edge(Vertex from, Vertex to)
	{
		super(from.getX(), from.getY(), to.getX(), to.getY());
		m_from = from;
		m_to = to;
	}

	public Line2D.Double getArrowSideL()
	{
		return m_arrowSideL;
	}

	public Line2D.Double getArrowSideR()
	{
		return m_arrowSideR;
	}

	public void free()
	{
		m_from.removeLink(this);
		m_to.removeLink(this);
	}

	public void refreshPos()
	{
		try
		{
			double x1 = m_from.getCenterX(), x2 = m_to.getCenterX(), y1 = m_from
					.getCenterY(), y2 = m_to.getCenterY();
			double dx = x2 - x1, dy = y2 - y1;
			double tga, alpha;
			if (dx == 0)
			{
				alpha = ((y2 > y1) ? Math.PI / 2 : -Math.PI / 2);
			}
			else
			{
				tga = dy / dx;
				alpha = Math.atan(tga);
			}

			// Pretends we have circle, not ellipse
			double offsetX = Math.abs(Vertex.VERTEX_HEIGHT / 2
					* Math.cos(alpha));
			double offsetY = Math.abs(Vertex.VERTEX_HEIGHT / 2
					* Math.sin(alpha));
			x1 += offsetX * Math.signum(dx);
			x2 -= offsetX * Math.signum(dx);
			y1 += offsetY * Math.signum(dy);
			y2 -= offsetY * Math.signum(dy);

			Point2D.Double p1 = new Point2D.Double(x1, y1);
			Point2D.Double p2 = new Point2D.Double(x2, y2);
			setLine(p1, p2);
			Point2D pal, par;
			if (dx == 0)
			{
				pal = new Point2D.Double(x2 - ARROW_LENGHT
						* Math.sin(ARROW_ANGLE), y2 - ARROW_LENGHT
						* Math.cos(ARROW_ANGLE)*Math.signum(dy));
				par = new Point2D.Double(x2 + ARROW_LENGHT
						* Math.sin(ARROW_ANGLE), y2 - ARROW_LENGHT
						* Math.cos(ARROW_ANGLE)*Math.signum(dy));
			}
			else
			{
				// Left and right points of arrow
				pal = new Point2D.Double(p2.x - ARROW_LENGHT
						* Math.cos(Math.PI - alpha + ARROW_ANGLE)
						* Math.signum(-dx), p2.y + ARROW_LENGHT
						* Math.sin(Math.PI - alpha + ARROW_ANGLE)
						* Math.signum(-dx));
				par = new Point2D.Double(p2.x - ARROW_LENGHT
						* Math.cos(Math.PI - alpha - ARROW_ANGLE)
						* Math.signum(-dx), p2.y + ARROW_LENGHT
						* Math.sin(Math.PI - alpha - ARROW_ANGLE)
						* Math.signum(-dx));
			}
			m_arrowSideL = new Line2D.Double(p2, pal);
			m_arrowSideR = new Line2D.Double(p2, par);
		}
		catch (ArithmeticException e) // in case dx == 0
		{
			e.printStackTrace();
		}
	}

	public Vertex getFrom()
	{
		return m_from;
	}

	@Override
	public int hashCode()
	{
		final int prime = 31;
		int result = 1;
		result = prime * result + ((m_from == null) ? 0 : m_from.hashCode());
		result = prime * result + ((m_to == null) ? 0 : m_to.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj)
	{
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Edge other = (Edge) obj;
		if (m_from == null)
		{
			if (other.m_from != null)
				return false;
		}
		else if (!m_from.equals(other.m_from))
			return false;
		if (m_to == null)
		{
			if (other.m_to != null)
				return false;
		}
		else if (!m_to.equals(other.m_to))
			return false;
		return true;
	}

	public Vertex getTo()
	{
		return m_to;
	}

	static public BasicStroke getStroke()
	{
		return m_stroke;
	}

	public Color getColor()
	{
		if (!m_selected)
		{
			return m_color;
		}
		else
		{
			return m_selectionColor;
		}
	}

	public void setColor(Color color)
	{
		m_color = color;
	}

	public void setSelected(boolean b)
	{
		m_selected = b;
	}
}
